package edu.nf.dbutil.converter;

import edu.nf.dbutil.TypeConverter;

import java.sql.Time;
import java.sql.Timestamp;
import java.time.*;
import java.util.Date;

/**
 * 将数据库不同类型的日期统一转换为java.util.Date或者是它的子类
 * @author yang
 * @date 2024-05-09
 */
public class DateConverter implements TypeConverter {
    /**
     * 当实体类字段属性的类型为老版本的时间类型时，会自动调用该实现类，若为其他类型，则会跳过
     * 判断实体对象的字段类型是否符合 "java.util.Date" 及其子类
     * @param type 转换类型
     * @return
     */
    @Override
    public boolean supports(Class<?> type) {
        //获取type的全限定类名
        String typeName = type.getName();
        return switch(typeName){
            case "java.util.Date",
                 "java.sql.Date",
                 "java.sql.Time",
                 "java.sql.Timestamp" -> true;
            default -> false;
        };
    }

    /**
     * 判断字段的时间类型并返回对应的时间类型对象
     * @param fieldType 字段的 Class 类型
     * @param value 结果集获取的数据
     * @return
     * 通过传入的字段 Class 对象获取其全限定类名 typeName（如 "java.util.Date"）
     * 根据自定义转换时间戳的方法 getTime(value) 将传入的数据库时间类型参数转换为时间戳格式
     * 判断 typeName 是否为 "java.util.Date"、"java.sql.Date"、"java.sql.Timestamp"、"java.util.Time" 的实例
     * 并通过对应的方法获取时间格式
     */
    @Override
    public Object convert(Class<?> fieldType, Object value) {
        //获取typeName的全限定类名
        String typeName = fieldType.getName();
        long time = getTime(value);
        return switch (typeName){
            case "java.util.Date" -> new Date(time);
            case "java.sql.Date" -> new java.sql.Date(time);
            case "java.sql.Timestamp" -> new Timestamp(time);
            case "java.sql.Time" -> new Time(time);
            default -> null;
        };
    }

    /**
     * 将日期都变成 long 的类型
     * @param value 需要转换的日期
     * @return 不同类型统一的时间戳对象
     * 将数据库不同类型的日期类型统一转换为 java.util.Date 并调用 getTime() 方法返回时间戳
     */
    private long getTime(Object value) {
        return switch(value){
            case Date date -> date.getTime();
            case LocalDateTime localDateTime -> localDateTimeToDate(localDateTime).getTime();
            case LocalDate localDate -> localDateToDate(localDate).getTime();
            case LocalTime localTime -> localTimeToDate(localTime).getTime();
            default -> throw new RuntimeException("Unable to convert" + value + "to Date");
        };
    }

    /**
     * 将LocalDateTime转换为Date
     * @param localDateTime
     * @return
     * 通过 ZoneId.systemDefault() 获取时区ID（Asia/Shanghai）
     * 通过调用 LocalDateTime 的方法 atZone(zoneId).toInstant() 获取瞬时时间（2024-05-10T11:59:45.200417700Z）
     * 最后返回 Date 类型时间对象（Fri May 10 19:59:45 CST 2024）
     */
    private Date localDateTimeToDate(LocalDateTime localDateTime){
        //获取当前时区Id
        ZoneId zoneId = ZoneId.systemDefault();
        //获取瞬时时间
        Instant instant = localDateTime.atZone(zoneId).toInstant();
        return Date.from(instant);
    }

    /**
     * 将LocalDate转换为Date
     * @param localDate
     * @return
     */
    private Date localDateToDate(LocalDate localDate){
        //获取当前时区Id
        ZoneId zoneId = ZoneId.systemDefault();
        //获取瞬时时间
        Instant instant = localDate.atStartOfDay(zoneId).toInstant();
        return Date.from(instant);
    }

    /**
     * 将LocalTime转换为Date
     * @param localTime
     * @return
     */
    private Date localTimeToDate(LocalTime localTime){
        //获取当前时区Id
        ZoneId zoneId = ZoneId.systemDefault();
        //获取瞬时时间
        Instant instant = localTime.atDate(LocalDate.now()).atZone(zoneId).toInstant();
        return Date.from(instant);
    }
}
